
package servidor;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Database {
    
    //private static Vector tagss = new Vector();
    
    private Vector tags; //vector de tags...la base esta indexada por tag
    private Vector titulos;  //esto sera una matriz
    private Vector paths;  //esto sera una matriz
    private Vector ids; //matriz de id de ingredientes  

    public Database() {
        
        tags = new Vector();
        titulos = new Vector();
        paths = new Vector();
        ids = new Vector();
        
    }
    
    public void setBase(Vector base){
        tags = (Vector)base.get(0);
        titulos =  (Vector)base.get(1);
        paths =  (Vector)base.get(2);
        ids =  (Vector)base.get(3);
    }
    
    public void loadIngredients( String directory ){
       
        Vector temp = new Vector();         
        
        File[] ingredients = Util.getIngredients(directory);
        
        
        try{
            for(int i = 0; i < ingredients.length ; i++){

                String ingredientXML = Util.readTextFile(ingredients[i].getAbsolutePath());

                Ingredient ing = Ingredient.fromXml(ingredientXML); 

                temp = ing.getTags();

                for( int j = 0; j < temp.size(); j++ ){
                    temp.setElementAt(((String)temp.get(j)).toLowerCase(), j); //pone los tags en minusculas
                    int index = tags.indexOf(temp.get(j)); //existe ese tag en el vector?
                    if( index >= 0 ){
                        ((Vector)titulos.get(index)).addElement(ing.getTitle().toLowerCase());
                        ((Vector)paths.get(index)).addElement(ingredients[i].getAbsolutePath().toLowerCase());
                        ((Vector)ids.get(index)).addElement(ing.getId()); 
                    }
                    else {
                        tags.addElement(((String)temp.get(j)).toLowerCase()); //agrega el nuevo tag
                        Vector titulo = new Vector();
                        titulo.addElement(ing.getTitle().toLowerCase()); //crea el vector de titulos y agrega
                        titulos.addElement(titulo); //agrega el nuevo vector de titulos de este tag, a la matriz de titulos
                        Vector path = new Vector();
                        path.addElement(ingredients[i].getAbsolutePath());
                        paths.addElement(path); //agrega el nuevo vector de paths de este tag, a la matriz de tags
                        Vector id = new Vector();
                        id.addElement(ing.getId());
                        ids.addElement(id);
                    }
                }

            }
        }catch(Exception e){
            System.out.println("Error. Carpeta de ingredientes no encontrada");
            e.printStackTrace();
            return ;
        }
        save();
    }
    
    public void refresh( String directory ){
        
        tags.removeAllElements();
        titulos.removeAllElements();
        paths.removeAllElements();
        ids.removeAllElements();
        
        this.loadIngredients(directory);
    }
    
    public void add( Vector temp, String titulo, String path, int id ){
        
         for( int j = 0; j < temp.size(); j++ ){
                int index = tags.indexOf(temp.get(j));
                if( index >= 0 ){
                    ((Vector)titulos.get(index)).addElement(titulo);
                    ((Vector)paths.get(index)).addElement(path);
                    ((Vector)ids.get(index)).addElement(id);
                }
                else {
                    tags.addElement(temp.get(j)); // el tag no estaba cargado..lo carga
                    //crea el vector de titulos para el nuevo tag
                    Vector tit = new Vector();
                    tit.addElement(titulo);
                    titulos.addElement(tit);
                    //crea el vector de paths para el nuevo tag
                    Vector pa = new Vector();
                    pa.addElement(path);
                    paths.addElement(pa);
                    //crea el vector de id para el nuevo tag
                    Vector i = new Vector();
                    i.addElement(id);
                    ids.addElement(i);
                }
                
            }
         save(); //graba en disco de pecho
    }
    
    //devuelve una matriz, en donde cada fila es un ingrediente
    public Vector simpleSearch( String tag ){
        tag = tag.toLowerCase();
        int index = tags.indexOf(tag);
        if( index >= 0 ){
            Vector temp = new Vector();
      
            temp.addElement(titulos.get(index));
            temp.addElement(paths.get(index));
            temp.addElement(ids.get(index));
        
            return temp;
        }
        return null;
    }
    
    public Vector multipleSearch( Vector<String> vectorTags){
        Vector lastResult, previous = null;
        for(int i = 0; i < vectorTags.size() ; i++) {
            lastResult = simpleSearch(vectorTags.get(i));
            previous = filter(lastResult, previous);
        }
        return previous;
    }
    
//    //borrar, solo pa probar
//    public static void main(String [] args){
//        Database db = new Database();
//        db.refresh("/home/tuza/facu/LIS/trunk/HowTo/ingredients");
//        
//        System.out.println(db.getIngredientPathById(2059857859));
        //Vector res = test.getDB().simpleSearch("a");
//        Vector<String> buscar = new Vector();
//        buscar.addElement("d");
//        buscar.addElement("a");
//        buscar.addElement("r");
//        buscar.addElement("c");
        
        //buscar.addElement("c");
//        Vector res = db.multipleSearch(buscar);
//
//        for(int i = 0; i < res.size() ; i++ ){
//            
//            System.out.println(" - " + i + " -\n");
//            Vector tem = (Vector) res.get(i);
//            for(int j = 0; j < ((Vector)tem.get(0)).size() ; j++ ){
//                //System.out.println(((Vector)tem.get(0)).size() + " elo");
//                System.out.println(((Vector)tem.get(0)).get(j));
//                System.out.println(((Vector)tem.get(1)).get(j));
//                System.out.println(((Vector)tem.get(2)).get(j));
//                //System.out.println(j + "--------\n");
//            }
//        
//        }
//    }
    
    //se le pasa el vector de resultados (last) de simplesearch, mas el vector o matriz ordenado de busqueda previa 
    //segun tags anteriores, y agrega o actualiza los resultados de last, en prev. Devuelve a prev ordenado 
    //segun numero de apariencias d cada tag
    //al pedo lo comento...no creo q lo entienda la proxima vez q lo agarre...mucho menos alguien ajeno
    //flor de bardo...
    public Vector filter(Vector last, Vector prev){
        if(prev != null){
            Vector<Integer> ides = (Vector<Integer>) last.get(2);
            for (int i = 0 ; i < ides.size() ; i++){
                int currentId = ides.get(i);
                boolean flagBreak = false;
                boolean flagFound = false;
                for(int j = prev.size() ; j > 0 ; j--){ //itera sobre los vectores, que alojan ing de acuerdo al numero de repeticiones DESCENDENTEMENTE...IMPORTANTE
                    if(flagFound){
                        break;
                    }
                    Vector repeatedJTimes =  (Vector) prev.get(j-1);  //trae matriz con resultados q aparecen j veces
                    //System.out.println(repeatedJTimes.get(2)); //debuggin purposes
                    Vector<Integer> idPrev = ((Vector<Integer>)repeatedJTimes.get(2)); //  tare el vector de id que se repite j veces
                    
                    for(int k = 0; k < idPrev.size(); k++ ){
                        if(currentId == idPrev.get(k)){
                            flagFound = true;
                            String title = (String)((Vector)repeatedJTimes.get(0)).get(k); //vector de titulos..obtiene el titulo k
                            String path = (String)((Vector)repeatedJTimes.get(1)).get(k); //vector de paths...obtiene el path k
                            
                            ((Vector)repeatedJTimes.get(0)).remove(k);
                            ((Vector)repeatedJTimes.get(1)).remove(k); //y lo remueve del vector de repeticion actual
                            idPrev.remove(k);
                            
                            k--; //como elimino elementos del medio, ahora decrementa k para evitar saltear
                            try{
                                Vector insertarEn = (Vector)prev.get(j);
                                ((Vector)insertarEn.get(0)).addElement(title) ; 
                                ((Vector)insertarEn.get(1)).addElement(path) ; 
                                ((Vector)insertarEn.get(2)).addElement(currentId) ; 
                            }
                            catch(ArrayIndexOutOfBoundsException e){ // si esta en el ultimo, crea un elemento nuevo..
                                Vector t = new Vector();
                                t.addElement(title);
                                Vector p = new Vector();
                                p.addElement(path);
                                Vector d = new Vector();
                                d.addElement(currentId);
                                
                                Vector temp = new Vector();
                                temp.addElement(t);
                                temp.addElement(p);
                                temp.addElement(d);
                                
                                prev.addElement(temp);
                            }
                        }
                        if(flagFound){
                            break;
                        }
                        
                    }
                }
                if(!flagFound){  //currentId no se encuentra anteriormente, se lo a?ade a prev[0]

                    ((Vector)((Vector)prev.get(0)).get(0)).addElement(((Vector)last.get(0)).get(i)); //setea el titulo del last[i] q no se encontro en prev
                    ((Vector)((Vector)prev.get(0)).get(1)).addElement(((Vector)last.get(1)).get(i)); //idem con path
                    ((Vector)((Vector)prev.get(0)).get(2)).addElement(((Vector)last.get(2)).get(i)); //idem con id
                    
                }
            }
            return prev;
        
        }
        else{
            if(last != null){
                prev = new Vector();
                prev.addElement(last);
            }
            return prev; 
        }
    }
    
    
    
   
   
    public Node toXml(){
      try
      {
          DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
          DocumentBuilder parser = fact.newDocumentBuilder();
          Document doc = parser.newDocument();

          return toXml( doc );
      }
      catch( Exception ex )
      {
         return null;
      }
  }
    
    public Node toXml( Document doc ){
       
      try {
      Node root = doc.createElement("Database");
      //doc.appendChild(root);

      for(int i=0;i< tags.size();i++){
         
          Node tag = doc.createElement((String)tags.get(i));
          
          
          for( int j=0; j < ((Vector)titulos.get(i)).size(); j++ ){
              Vector tit = ((Vector)titulos.get(i));
              Vector pa = ((Vector)paths.get(i));
              Vector t = ((Vector)ids.get(i));
              Node ingredient = doc.createElement("ingredient");
              Node title = doc.createElement("title");
              Node p = doc.createElement("path");
              Node ide = doc.createElement("id");
              title.appendChild(doc.createTextNode((String)tit.get(j)));
              p.appendChild(doc.createTextNode((String)pa.get(j)));
              ide.appendChild(doc.createTextNode(String.valueOf(t.get(j))));
              ingredient.appendChild(title);
              ingredient.appendChild(p);
              ingredient.appendChild(ide);
              
              tag.appendChild(ingredient);
              
          }
          root.appendChild(tag);

      }
      return root;

    } catch (Exception ex) {
      ex.printStackTrace();
      return null;
    }
  } 
    
  public void save(){
      File file = new File("database");  
      file.mkdir();
      String fileName = "database/database.xml";
        try
        {
            OutputStream fout= new FileOutputStream(fileName);
            TransformerFactory transFactory = TransformerFactory.newInstance();
            Transformer transformer = transFactory.newTransformer();
            DOMSource source = new DOMSource(this.toXml());
            StreamResult result = new StreamResult(fout);
            transformer.transform(source, result);
        }
        catch( Exception e ){  };
    }
  
  public static Vector fromXml( String str )
    {
        InputStream is = new ByteArrayInputStream( str.getBytes() );
        
        try
        {
            DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
            DocumentBuilder parser = fact.newDocumentBuilder();
            Document doc = parser.parse( is );

            Vector tags = fromXml( doc );
            return tags;
        }
        catch( Exception ex )
        {
            ex.printStackTrace();
        }
        return null;
    }
  
  public static Vector fromXml( Document doc )
    {
      
        Vector tags = new Vector();
        Vector titulos = new Vector();
        Vector paths = new Vector();
        Vector ids = new Vector();
        
        Vector base = new Vector();
      
        try
        {
            NodeList nodes = doc.getElementsByTagName( "Database" );
            
            Node node = nodes.item( 0 );
            NodeList nodeListTags = node.getChildNodes();
            for(int j=0;j<nodeListTags.getLength();j++){
                Node tagNode = nodeListTags.item( j );
                tags.addElement(tagNode.getNodeName());
                
                NodeList ingrNodeList = tagNode.getChildNodes();
                titulos.addElement(new Vector());
                paths.addElement(new Vector());
                ids.addElement(new Vector());
                
                for(int i = 0; i < ingrNodeList.getLength(); i++){
                    Element ingrElement = (Element)ingrNodeList.item(i);
                    ((Vector)titulos.get(j)).addElement(ingrElement.getElementsByTagName("title").item(0).getNodeValue()) ;
                    ((Vector)paths.get(j)).addElement(ingrElement.getElementsByTagName("path").item(0).getNodeValue());
                    ((Vector)ids.get(j)).addElement(Integer.parseInt(ingrElement.getElementsByTagName("id").item(0).getNodeValue()));
                }
            }
        }
        catch( Exception ex )
        {
            ex.printStackTrace();
            return null;
        }
        
        base.addElement(tags);
        base.addElement(titulos);
        base.addElement(paths);
        base.addElement(ids);
        
        return base;
  }
    
        
    //busca en la matriz de ids hasta encontrar, y devuelve el path de la matriz de paths
    public String getIngredientPathById(int buscarId){
        for(int i = 0 ; i < ids.size() ; i++){
            Vector d = (Vector) ids.get(i);
            for(int j = 0; j < d.size() ; j++){
                int currentId= (Integer) d.get(j);
                if(currentId == buscarId){
                    return ((String)((Vector)paths.get(i)).get(j)); //devuelve el path para esa id
                }
            }
        }
        return null;
    }
    
}

